<?php

if (!defined('IN_TG')) {
    exit('Access decline');
}

/**
 * B64Secret类 （机密数据不使用此类）主要以base64_encode编码解码的数据，仅在传输些不涉密数据时使用
 * 如：URL传值，接口数据传递等，本可明文传递，为了预防数据被篡改，而进行编码数据源 并生成 验证秘钥
 * 虽然其中加入一些混淆字符串 和 数据完整性验证秘钥(不可解密)，但直接通过base64_decode还是能解码基本还原原始数据
 * 验证秘钥的重要性：在解码取得原始数据后，重新生成秘钥与传递秘钥进行对比校验，如果失败则直接丢弃，不再进行使用。
 * 使用方式 编码：$seKey = new B64Secret();   $seKey->enRndCode('字符串或数组')
 * 解码：$seKey = new B64Secret();   $seKey->deRndCode('5a2X54860cd8d7335a9347c852c4bb7399f9914aee232982389b7cad3ef3a3efd4e0d6ym5Liy5oiW5pWw57uE.CB067240FA44B23E4B80C33A076D1989643EFE1620131AFE5610EE6CC696E0D3.173321342964b75c7f2bb83')
 */
class B64Secret
{
    // 加密(验证秘钥)时使用的安全验证字符，在使用数据时需要此字符来验证数据的完整性
    private $secretKey = "DreamUrlSecret";

    /**
     * enRndCode($str)编码并加密以URL传值或写Cookie的字符串
     * @param string $str 要加密的字符，格式：{"userid":"103","username":"dream","valid_code":"94d410b2458d2f","dmf":"192.168.3.13","expt":"1644282355"}
     * @return string 编码后的字符，格式: a.b.c
     */
    public function enRndCode($str)
    {
        //第1位的数据base64加密
        $info_b64str = $this->b64Encode($str);
        //第2位的数据加密为安全验证字符串，用于取得真实数据后再对比
        $sign_str = strtoupper(hash("sha256", $str . $this->secretKey));
        //第3位的安全验证使用的随机字符
        $rand_str = uniqid(rand(), false);
        //第3位的随机字符使用str256Hash加密成64位字符，用于随机插入到第1位的字符串中
        //在decrypt_json_str解密中要将第3位str256Hash加密后再替换掉第1位中的字符串
        $encrypt_rand_str = $this->str256Hash($rand_str, false);
        //将64位字符，随机插入到加密的使用数据中
        $info_mixstr = $this->inStrRand($info_b64str, $encrypt_rand_str, 'utf-8');
        //组合成 信息+秘钥+随机字符的长串
        $str_all = $info_mixstr . "." . $sign_str . "." . $rand_str;
        return $str_all;
    }

    /**
     * deRndCode($str)解码以URL传值或写Cookie的字符串
     * @param string $str 要解码的字符，格式: a.b.c
     * @return string 解码成功返回Json数据，失败返回false
     */
    public function deRndCode($str)
    {
        $tokens = explode('.', $str);
        if (count($tokens) != 3) {
            return false;
        }
        list($info_mixstr, $sign_str, $rand_str) = $tokens;
        //先将$rand_str加密后再替换掉
        $encrypt_rand_str = $this->str256Hash($rand_str, false);
        //将含有64位字符的串替换，这相当于其它人获取了字符串后使用base64解码的二次安全处理
        $info_b64str = str_replace($encrypt_rand_str, "", $info_mixstr);
        //使用base64解码，得到json字符串
        $get_info_str = $this->b64Decode($info_b64str);
        //将Json格式转为数组，得到原始数据
        $original_arr = json_decode($get_info_str, JSON_OBJECT_AS_ARRAY);
        //将解码后的json字符串和秘钥一起加密后，再和获取的第2位字符串进行对比
        $newsign = strtoupper(hash("sha256", $get_info_str . $this->secretKey));
        //输出前后2次秘钥对比
        if ($newsign != $sign_str) {
            return false;
        }
        //验证过期时间
        if (isset($original_arr['expt']) && $original_arr['expt'] < time()) {
            return false;
        }
        //验证来源和当前域名是否相同
        if (isset($original_arr['dmf']) && $original_arr['dmf'] != $_SERVER["SERVER_NAME"]) {
            return false;
        }
        //返回解码后的array原始数据
        return $original_arr;
    }

    /**
     * b64Encode()将数据进行base64编码
     * @param string $input 要编码的数据
     * @return string 编码后的字符串
     */
    private function b64Encode($input)
    {
        return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
    }

    /**
     * b64Decode()将base64编码的数据解码
     * @param string $input 要解码的字符串
     * @return string 解码后的原始数据
     */
    private function b64Decode($input)
    {
        $remainder = strlen($input) % 4;
        if ($remainder) {
            $addlen = 4 - $remainder;
            $input .= str_repeat('=', $addlen);
        }
        return base64_decode(strtr($input, '-_', '+/'));
    }

    /**
     * inStrRand 向一个字符串随机位置插入一个字符串
     * @param string $oldstr 老字符串
     * @param string $instr 插入的字符串
     * @param string $encoding 字符串编码
     * @return string 新字符串
     */
    private function inStrRand($oldstr, $instr, $encoding)
    {
        //计算长度
        $len = mb_strlen($oldstr, $encoding);
        //生成随机位置数字
        $insert_point = mt_rand(1, $len - 1);
        //插入位置前面的字符串
        $pre_str = mb_substr($oldstr, 0, $insert_point, $encoding);
        //插入位置后面的字符串
        $after_str = mb_substr($oldstr, $insert_point, $len - $insert_point, $encoding);
        //链接前面+指定字符+后面的字符串
        $newstr = $pre_str . $instr . $after_str;
        return $newstr;
    }

    /**
     * str256Hash 文字sha256加密
     * @param string $data 要加密的文字
     * @param boolean $rawOutput 返回加密方式：true返回二进制数据，false返回字符串
     * @return false|string 失败时返回false，成功返回string 或 Binary
     */
    private function str256Hash($data, $rawOutput = false)
    {
        if (!is_scalar($data)) {
            return false;
        }
        $data = (string)$data;
        $rawOutput = !!$rawOutput;
        return hash('sha256', $data, $rawOutput);
    }

}

    // //构造方法，用来初始化数据，链接数据库
    // public function __construct()
    // {
    //     $this->con_mysql();
    // }

?>